package cn.ekuma.odbo.mobile.category.ui

import android.animation.Animator
import android.animation.AnimatorInflater
import android.content.Context
import android.os.Build
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.animation.Animation
import android.view.animation.AnimationUtils
import androidx.annotation.AnimRes
import androidx.annotation.AnimatorRes
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import cn.ekuma.odbo.mobile.category.base.CategoryItemWarpDiff
import com.gitee.wsl.struct.category.CategoryItemWarp
import com.gitee.wsl.struct.tree.CategoryTreeNode
import com.gitee.wsl.struct.tree.TreeNode
import java.lang.ref.WeakReference

abstract class AbstractCategoryItemAdapter(val onTreeNodeListener:OnTreeNodeListener) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
    //private  final String KEY_IS_EXPAND = "IS_EXPAND"
    protected var displayNodes = mutableListOf<CategoryItemWarp>()
    protected var padding:Int = 30
    protected var toCollapseChild:Boolean=false

    protected var holderBinderMap = mutableMapOf<Int,TreeViewBinder<RecyclerView.ViewHolder>>()

    protected lateinit var rootNode:CategoryItemWarp

    /**
     * 从nodes的结点中寻找展开了的非叶结点，添加到displayNodes中。
     *
     * @param nodes 基准点
     */
    private fun findDisplayNodes( nodes:List<CategoryItemWarp>) {
        nodes.forEach {node->
            displayNodes.add(node);
            if (!node.isLeaf )
                findDisplayNodes(node.subItemList);
        }
    }

    override fun getItemCount(): Int {
        return displayNodes.size
    }

    override fun getItemViewType(position: Int): Int {
        displayNodes[position].let {
            return it.categoryItemType
        }
        //return super.getItemViewType(position)
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        val viewBinder = getViewHolderBinder(viewType)
        val v = LayoutInflater.from(parent.context).inflate(viewBinder.layoutId, parent, false)

       return viewBinder. provideViewHolder(v)
    }

    override fun onBindViewHolder( holder: RecyclerView.ViewHolder, position: Int) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
            holder.itemView.setPaddingRelative(displayNodes[position].height() * padding, 3, 3, 3);
        }else {
            holder.itemView.setPadding(displayNodes[position].height() * padding, 3, 3, 3);
        }

        holder.itemView.setOnClickListener(View.OnClickListener{v->
                val selectedNode = displayNodes[holder.layoutPosition]
                // Prevent multi-click during the short interval.
                try {
                    val lastClickTime:Long = holder.itemView.tag as Long
                    if (System.currentTimeMillis()-lastClickTime < 500)
                        return@OnClickListener
                } catch ( e:Exception) {
                    holder.itemView.tag = System.currentTimeMillis()
                }
            holder.itemView.tag = System.currentTimeMillis()
                if (onTreeNodeListener != null && onTreeNodeListener.onClick(selectedNode, holder))
                    return@OnClickListener
                if (selectedNode.isLeaf)
                    return@OnClickListener
                // This TreeNode was locked to click.
                //if (selectedNode.isLocked()) return
                val positionStart = displayNodes.indexOf(selectedNode) + 1
                if (!selectedNode.isExpand) {
                    notifyItemRangeInserted(positionStart, addChildNodes(selectedNode, positionStart))
                } else {
                    notifyItemRangeRemoved(positionStart, removeChildNodes(selectedNode, true))
                }
        })

        val viewBinder = getViewHolderBinder(getItemViewType(position))
        viewBinder.bindView(holder,position,displayNodes[position])

        initAnimate(holder,position)
    }

    open fun getViewHolderBinder(viewType: Int):TreeViewBinder<RecyclerView.ViewHolder>{
       holderBinderMap[viewType]?.let {
            return it
        }
        val holderBinder=createViewHolderBinder(viewType)
        holderBinderMap[viewType]=holderBinder
        return holderBinder
    }

    abstract fun createViewHolderBinder(viewType: Int):TreeViewBinder<RecyclerView.ViewHolder>


    open fun initAnimate(itemHolder: RecyclerView.ViewHolder, position: Int){}


    /**动画延迟的时间, 也是动画是否已经执行过的标识
     *
     */
    var itemAnimateDelay: Long = -1

     fun _initItemAnimate(itemHolder: RecyclerView.ViewHolder,itemAnimateRes:Int,_itemAnimateDelay: Long?=null) {
        if (itemAnimateRes != 0) {
            itemAnimateDelay =_itemAnimateDelay?: itemAnimateDelay
            //L.w("_itemAnimateDelay:$_itemAnimateDelay")
            if (itemAnimateDelay >= 0) {
                val animation = animationOf(itemHolder.itemView.context, itemAnimateRes)
                if (animation != null) {
                    animation.startOffset = itemAnimateDelay
                    itemHolder.itemView.startAnimation(animation)
                    itemAnimateDelay = -2 //动画已执行标识
                } else {
                    val animator = animatorOf(itemHolder.itemView.context, itemAnimateRes)
                    if (animator != null) {
                        animator.setTarget(itemHolder.itemView)
                        animator.startDelay = itemAnimateDelay
                        itemHolder.itemView.setAnimator(animator)
                        animator.start()
                        itemAnimateDelay = -2 //动画已执行标识
                    }
                }
            }
        }
    }


    private fun addChildNodes( pNode:CategoryItemWarp,  startIndex:Int):Int {
        val childList = pNode.subItemList
        var addChildCount = 0
        childList.forEach { treeNode->
            displayNodes.add(startIndex + addChildCount++, treeNode as CategoryItemWarp)
            if (treeNode.isExpand) {
                addChildCount += addChildNodes(treeNode, startIndex + addChildCount);
            }
        }

        if (!pNode.isExpand)
            pNode.toggle();
        return addChildCount;
    }

    private fun removeChildNodes( pNode:CategoryItemWarp):Int {
        return removeChildNodes(pNode, true);
    }

    private fun removeChildNodes( pNode:CategoryItemWarp,  shouldToggle:Boolean):Int {
        if (pNode.isLeaf)
            return 0;
        val childList = pNode.subItemList;
        var removeChildCount = childList.size;
        displayNodes.removeAll(childList);
        childList.forEach { child->
            if (child.isExpand) {
                if (toCollapseChild)
                    child.toggle()
                removeChildCount += removeChildNodes(child as CategoryItemWarp, false);
            }
        }

        if (shouldToggle)
            pNode.toggle()
        return removeChildCount
    }


     fun ifCollapseChildWhileCollapseParent( toCollapseChild:Boolean) {
        this.toCollapseChild = toCollapseChild
    }

    fun refresh(treeNodes:List<CategoryItemWarp>) {
        displayNodes.clear()
        findDisplayNodes(treeNodes)
        notifyDataSetChanged()
    }

    fun subject(rootNode:CategoryItemWarp){
        this.rootNode=rootNode
        refresh(rootNode.categoryItemList() as List<CategoryItemWarp>)
    }

    fun getDisplayNodesIterator():Iterator<CategoryItemWarp> {
        return displayNodes.iterator()
    }

    //**
    //* collapse all root nodes.
    //**
    fun collapseAll() {
        // Back up the nodes are displaying.
        val temp = backupDisplayNodes()
        //find all root nodes.
        val roots =  mutableListOf<CategoryItemWarp>()

        displayNodes.forEach {displayNode->
            if (displayNode.isRoot)
                roots.add(displayNode);
        }

        roots.forEach {node->
            if (node.isExpand)
                removeChildNodes(node);
        }

        notifyDiff(temp);
    }


    private fun backupDisplayNodes():List<CategoryItemWarp> {
        val temp = mutableListOf<CategoryItemWarp>()

        displayNodes.forEach { node ->
            try {
                //temp.add(node.clone() as CategoryItemWarp)
                temp.add(node as CategoryItemWarp)
            } catch (e:CloneNotSupportedException) {
                temp.add(node)
            }
        }

        return temp
    }

     fun collapseNode( pNode:CategoryItemWarp) {
        val temp = backupDisplayNodes()
        removeChildNodes(pNode)
        notifyDiff(temp)
    }

     fun collapseBrotherNode(pNode:CategoryItemWarp) {
        val temp = backupDisplayNodes()

         if(pNode.isRoot){
             val roots= mutableListOf<CategoryItemWarp>()
             displayNodes.forEach {displayNode->
                 if (displayNode.isRoot)
                     roots.add(displayNode)
             }
             roots.forEach {node->
                 if (node.isExpand && !node.equals(pNode))
                     removeChildNodes(node)
             }
         }else{
             var parent = pNode.parent() ?: return

             parent as CategoryItemWarp

             val childList = parent.subtrees()

             childList.forEach { node ->
                 node as CategoryItemWarp
                 if (node != pNode || node.isExpand)
                    removeChildNodes(node)
             }
         }

        notifyDiff(temp)
    }

    val categoryItemDiff by lazy {
       CategoryItemWarpDiff()
    }

    private fun notifyDiff( temp:List<CategoryItemWarp>) {
        val diffResult = DiffUtil.calculateDiff(object: DiffUtil.Callback() {
            override fun getOldListSize(): Int {
                return temp.size
            }

            override fun getNewListSize(): Int {
                return displayNodes.size
            }

            override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
                return categoryItemDiff.areItemsTheSame(temp[oldItemPosition],displayNodes[newItemPosition])
            }

            override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
                return categoryItemDiff.areContentsTheSame(temp[oldItemPosition],displayNodes[newItemPosition])
            }
        })
        diffResult.dispatchUpdatesTo(this)
    }


    /*@Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position, List<Object> payloads) {
        if (payloads != null && !payloads.isEmpty()) {
            Bundle b = (Bundle) payloads.get(0);
            for (String key : b.keySet()) {
                switch (key) {
                    case KEY_IS_EXPAND:
                    if (onTreeNodeListener != null)
                        onTreeNodeListener.onToggle(b.getBoolean(key), holder);
                    break;
                }
            }
        }
        super.onBindViewHolder(holder, position, payloads);
    }

    @Override
    public void onBindViewHolder(final RecyclerView.ViewHolder holder, int position) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
            holder.itemView.setPaddingRelative(displayNodes.get(position).getHeight() * padding, 3, 3, 3);
        }else {
            holder.itemView.setPadding(displayNodes.get(position).getHeight() * padding, 3, 3, 3);
        }
        holder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                TreeNode selectedNode = displayNodes.get(holder.getLayoutPosition());
                // Prevent multi-click during the short interval.
                try {
                    long lastClickTime = (long) holder.itemView.getTag();
                    if (System.currentTimeMillis() - lastClickTime < 500)
                        return;
                } catch (Exception e) {
                    holder.itemView.setTag(System.currentTimeMillis());
                }
                holder.itemView.setTag(System.currentTimeMillis());

                if (onTreeNodeListener != null && onTreeNodeListener.onClick(selectedNode, holder))
                    return;
                if (selectedNode.isLeaf())
                    return;
                // This TreeNode was locked to click.
                if (selectedNode.isLocked()) return;
                boolean isExpand = selectedNode.isExpand();
                int positionStart = displayNodes.indexOf(selectedNode) + 1;
                if (!isExpand) {
                    notifyItemRangeInserted(positionStart, addChildNodes(selectedNode, positionStart));
                } else {
                    notifyItemRangeRemoved(positionStart, removeChildNodes(selectedNode, true));
                }
            }
        });
        for (TreeViewBinder viewBinder : viewBinders) {
        if (viewBinder.getLayoutId() == displayNodes.get(position).getContent().getLayoutId())
            viewBinder.bindView(holder, position, displayNodes.get(position));
    }
    }


    private void notifyDiff(final List<TreeNode> temp) {
        DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new DiffUtil.Callback() {
            @Override
            public int getOldListSize() {
                return temp.size();
            }

            @Override
            public int getNewListSize() {
                return displayNodes.size();
            }

            // judge if the same items
            @Override
            public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
                return TreeViewAdapter.this.areItemsTheSame(temp.get(oldItemPosition), displayNodes.get(newItemPosition));
            }

            // if they are the same items, whether the contents has bean changed.
            @Override
            public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
                return TreeViewAdapter.this.areContentsTheSame(temp.get(oldItemPosition), displayNodes.get(newItemPosition));
            }

            @Nullable
            @Override
            public Object getChangePayload(int oldItemPosition, int newItemPosition) {
                return TreeViewAdapter.this.getChangePayload(temp.get(oldItemPosition), displayNodes.get(newItemPosition));
            }
        });
        diffResult.dispatchUpdatesTo(this);
    }

    private Object getChangePayload(TreeNode oldNode, TreeNode newNode) {
        Bundle diffBundle = new Bundle();
        if (newNode.isExpand() != oldNode.isExpand()) {
            diffBundle.putBoolean(KEY_IS_EXPAND, newNode.isExpand());
        }
        if (diffBundle.size() == 0)
            return null;
        return diffBundle;
    }


    */

}

abstract class OnTreeNodeListenerAdapter:OnTreeNodeListener{
    override fun onClick(
        node: CategoryItemWarp,
        holder: RecyclerView.ViewHolder
    ): Boolean {
        return false
    }

    override fun onToggle(isExpand: Boolean, holder: RecyclerView.ViewHolder) {

    }

    override fun initLoadData(): List<TreeNode> {
        return emptyList()
    }

    override fun categoryLazyLoadData(categoryTreeNode: CategoryTreeNode) {

    }
}

interface OnTreeNodeListener {
    /**
     * called when TreeNodes were clicked.
     * @return weather consume the click event.
     */
    fun  onClick(node:CategoryItemWarp, holder : RecyclerView.ViewHolder):Boolean

    /**
     * called when TreeNodes were toggle.
     * @param isExpand the status of TreeNodes after being toggled.
     */
    fun onToggle( isExpand:Boolean,  holder:RecyclerView.ViewHolder)


    fun initLoadData():List<TreeNode>

    fun categoryLazyLoadData(categoryTreeNode: CategoryTreeNode)

}


/**从指定资源id中, 加载动画[Animation]*/
fun animationOf(context: Context, @AnimRes id: Int): Animation? {
    try {
        if (id == 0 || id == -1) {
            return null
        }
        return AnimationUtils.loadAnimation(context, id)
    } catch (e: Exception) {
        //e.printStackTrace()
        //L.w(e.message)
        return null
    }
}

/**从指定资源id中, 加载动画[Animator]*/
fun animatorOf(context: Context, @AnimatorRes id: Int): Animator? {
    try {
        if (id == 0 || id == -1) {
            return null
        }
        return AnimatorInflater.loadAnimator(context, id)
    } catch (e: Exception) {
        //e.printStackTrace()
        //L.w(e.message)
        return null
    }
}

const val lib_tag_animator=999515

fun View.setAnimator(animator: Animator) {
    setTag(lib_tag_animator, WeakReference(animator))
}

/**取消动画[Animator]*/
fun View.cancelAnimator() {
    val tag = getTag(lib_tag_animator)
    var animator: Animator? = null
    if (tag is WeakReference<*>) {
        val any = tag.get()
        if (any is Animator) {
            animator = any
        }
    } else if (tag is Animator) {
        animator = tag
    }
    animator?.cancel()
}